package models;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;

import play.db.jpa.Model;

/**
 * Tag Entity in order to manage creating tags and handle tagging and untagging
 * for ideas and topics
 * 
 * @author Ghada Abu Shady
 */

@Entity
public class Tag extends Model {
	/**
	 * The name of the tag
	 * 
	 * @author Ghada Abu Shady
	 */
	public String name;
	public Long viewCount;

	/**
	 * The Users that are subscribed to a tag. A tag can have many users
	 * Subscribed to
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToMany
	public List<User> subscribers;

	/**
	 * The ideas that belongs to a certain tag. A tag can have many ideas
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToMany(cascade = CascadeType.ALL)
	public Set<Idea> Ideas;

	/**
	 * The topics that belongs to a certain tag. A tag can have many topics
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToMany(cascade = CascadeType.ALL)
	public Set<Topic> Topics;

	/**
	 * The organization that the tag belongs to. Many tags can belong to a
	 * certain organization
	 * 
	 * @author Ghada Abu Shady
	 */
	@ManyToOne
	public Organization organization;
	/**
	 * the tag can have many relations & the every relation includes more than
	 * one tag
	 * 
	 * @author Menna Magdy
	 */
	@ManyToMany
	public List<Relation> relationList;

	/**
	 * constructor that handles creating a new object Tag
	 * 
	 * added log statement
	 * 
	 * @param name
	 *            name of the tag
	 * @param org
	 *            organization that the tag belongs to
	 * 
	 * @author Ghada Abu Shady
	 * @author Mai Selwanes
	 */
	public Tag(String name, Organization org) {
		this.name = name;
		this.subscribers = new ArrayList<User>();
		this.relationList = new ArrayList<Relation>();
		this.organization = org;
		this.Ideas = new TreeSet<Idea>();
		this.Topics = new TreeSet<Topic>();
		User userNull = null;

		Log log = new Log(org, userNull, " A tag named " + name
				+ " has been created in organization: " + org.name + ".");
		log.save();
		org.tags.add(this);
	}

	/**
	 * getAllTags method returns all the tags in the System
	 * 
	 * @author Ghada Abu Shady
	 */
	public List<Tag> getAllTags() {
		List<Tag> tagList = Tag.findAll();
		this.save();
		return (tagList);
	}

	/**
	 * edits a tag, by taking a new name for the selected tag as an input and
	 * replaces the old name, one only if the tag has no ideas tagged to it and
	 * return true if the tag was edited successfully
	 * 
	 * added log statement
	 * 
	 * @param tag_name
	 *            the new name of the tag
	 * @author nesreen el gohary
	 * @author Mai Selwanes
	 */
	public boolean editTag(String tagName) {

		if (tagName.matches("[a-zA-Z]*")) {
			if (this.Ideas.isEmpty()) {
				User userNull = null;
				Log log = new Log(organization, userNull, " A tag named " + name
						+ " has been edited and became " + tagName
						+ " in organization: " + organization.name + ".");
				log.save();
				this.name = tagName;
				return true;
			} else
				return false;

		} else {
			return false;
		}

	}

	/**
	 * deletes a tag, one only if the tag has no ideas tagged to it and return
	 * true if the tag was deleted successfully
	 * 
	 * @author Nesreen elgohary
	 */
	public boolean deleteTag() {

		if (this.Ideas.isEmpty()) {
			User userNull = null;
			Log log = new Log(organization, userNull, " A tag named " + name
					+ " has been deleted from organization: " + organization.name + ".");
			log.save();
			this.organization.tags.remove(this);
			return true;
		} else
			return false;
	}

	/**
	 * ideasInTag method returns all ideas that are found in a certain tag
	 * 
	 * @param tag
	 *            the tag that is searched for his ideas
	 * 
	 * @author Ghada Abu Shady
	 */
	public Set<Idea> ideasInTag(String tag) {
		Tag tag1 = Tag.find("byName", "tag").first();
		return tag1.Ideas;
	}

	/**
	 * AddUsertoSubscribers is a method that takes a user as a param and adds it
	 * to the list of subscriber of this tag
	 * 
	 * added log statement
	 * 
	 * @param User
	 *            that should be added
	 * 
	 * @author Sara Assem
	 * @author Mai Selwanes
	 */
	public void addUserToSubscribers(User user) {
		subscribers.add(user);
		Log log = new Log(this.organization, user,
				" has been added to the list of subscribers of the tag of title: "
						+ this.name + " in organization " + this.organization.name);
		log.save();
	}

	/**
	 * RemoveUserFromSubscribers is a method that takes a user as a param and
	 * removes it from the list of subscriber of this tag
	 * 
	 * added log statement
	 * 
	 * @param User
	 *            that should be removed
	 * 
	 * @author Sara Assem
	 * @author Mai Selwanes
	 */
	public void removeUserFromSubscribers(User user) {
		subscribers.remove(user);
		Log log = new Log(this.organization, user,
				" has been removed from the list of subscribers of the tag of title: "
						+ this.name + " in organization " + this.organization.name);
		log.save();
	}

	/**
	 * The below method takes the parameters which is defined below and finds
	 * what is matching the search parameter in the database and initializes two
	 * variables one named count and one named tags which is a list of tags and
	 * return this list of tags as well as the count as new SearchResultTag
	 * 
	 * @param search
	 * @param page
	 * @return SearchResultTag
	 * @author ismail omar
	 */
	public static SearchResultTag searchForTag(String search, Integer page) {
		// String likeSearch = "%" + search + "%";
		Long count = count("name LIKE?", search.toLowerCase());
		List<Tag> tags = find("LOWER(name) LIKE?", search.toLowerCase()).fetch(
				page, 20);
		return new SearchResultTag(tags, count);
	}

	/**
	 * Method search in Tags returns the list of Tags with names that match the
	 * given string parameter search to search the keyword that will be used to
	 * be fetched from the database
	 * 
	 * @author Ahmed Ammar
	 */
	public static List<Tag> search(String search) {
		String likeSearch = search.toLowerCase();
		return Tag.find("LOWER(name) LIKE ?", likeSearch).fetch();
	}

	/**
	 * this method s whether the user is in the subscriber list of the tag or
	 * not
	 * 
	 * @param User
	 *            user the user that we want to check is he in the list or not .
	 *            
	 * @author Sara Assem
	 */
	public boolean userinSubscribers(User user) {

		for (User subscriber : this.subscribers) {
			if (user == subscriber)
				return true;

		}
		return false;
	}

	/**
	 * The below method performs a search in the database in Tag table according
	 * to the two search words that are inputs for this method
	 * 
	 * @param search
	 *            the first search word
	 * @param searchOne
	 *            the second search word
	 * @param page
	 * @author ismail omar
	 */
	public static SearchResultTag searchForTagOne(String search,
			String searchOne, Integer page) {

		Long count = count("name LIKE? OR name LIKE?", search.toLowerCase(),
				searchOne.toLowerCase());
		List<Tag> tags = find("LOWER(name) LIKE? OR LOWER(name) LIKE?",
				search.toLowerCase(), searchOne.toLowerCase()).fetch(page, 20);
		return new SearchResultTag(tags, count);
	}
	/**The below method performs a search in the database in Tag table according
	 * to the three search words that are inputs for this method
	 * 
	 * @param search
	 * 			First search word
	 * @param searchOne
	 * 			Second search word
	 * @param searchTwo
	 * 			Third search word
	 * @param page
	 * @author ismail omar
	 */
	public static SearchResultTag searchForTagTwo(String search,
			String searchOne, String searchTwo, Integer page) {

		Long count = count("name LIKE? OR name LIKE? OR name LIKE?",
				search.toLowerCase(), searchOne.toLowerCase(),
				searchTwo.toLowerCase());
		List<Tag> tags = find(
				"LOWER(name) LIKE? OR LOWER(name) LIKE? OR LOWER(name) LIKE?",
				search.toLowerCase(), searchOne.toLowerCase(),
				searchTwo.toLowerCase()).fetch(page, 20);
		return new SearchResultTag(tags, count);
	}
	/**The below method performs a search in the database in Tag table according
	 * to the four search words that are inputs for this method
	 * 
	 * @param search
	 * 			First search word	
	 * @param searchOne
	 * 			Second search word
	 * @param searchTwo
	 * 			Third search word
	 * @param searchThree
	 * 			Fourth search word
	 * @param page
	 * @author ismail omar
	 */
	public static SearchResultTag searchForTagThree(String search,
			String searchOne, String searchTwo, String searchThree, Integer page) {

		Long count = count(
				"name LIKE? OR name LIKE? OR name LIKE? OR name LIKE?",
				search.toLowerCase(), searchOne.toLowerCase(),
				searchTwo.toLowerCase(), searchThree.toLowerCase());
		List<Tag> tags = find(
				"LOWER(name) LIKE? OR LOWER(name) LIKE? OR LOWER(name) LIKE? OR LOWER(name) LIKE?",
				search.toLowerCase(), searchOne.toLowerCase(),
				searchTwo.toLowerCase(), searchThree.toLowerCase()).fetch(page,
				20);
		return new SearchResultTag(tags, count);
	}
	/**
	 * This method takes the user id and returns true if the type of the organization
	 * that he's searching in is public ,
	 * OR False if the organization is private/Closed and he's Not a member in it and True otherwise
	 * @param userId
	 *            the user's id
	 * @return
	 *       returns the True or False
	 *       @author Nourhan El-Nehlawy
	 */
	public boolean checkType(Long userId){
		if(!this.organization.type.matches("Public")){
			User user = User.findById(userId);
				if(this.organization.checkUser(user)){
					return true;
				}
				else return false;
		}
		else return true;
	}

}
